Git tutorial

Hiromu Asahina

Febrary 2019

Git

そもそもGitってなんだよ

プログラムのソースコードなどの変更履歴を記録・追跡するための分散型バージョン管理システムだよ.

作った人

リーナス・トーバルズ

  • Linux作った人(GitはLinux開発のためのツールとして作ったらしい)
  • Linuxは修論で作ったらしい
  • Gitは2週間くらいで作ったらしい
  • 開発当時,有名なOSの教授とお互いのOSを貶し合ってめちゃくちゃ喧嘩したのが伝説になっている
  • 優しい終身の独裁者(OSSコミュニティのリーダをこう呼ぶ)の一人
  • 優しい…

バージョン管理システムでできること

  • file変更履歴(version)を記録する
  • 特定のversionを復元する
  • 複数人が同じファイルを同時に編集する
  • version間の差分を見る

などなど…

バージョン管理システムが使える場面

  • source codeの管理 (メイン)
  • Shellなどの設定ファイルの管理(いわゆるDotfiles)
  • 小説や論文などの原稿の管理

などなど…

version(版)という概念のあるものすべてに応用可能

なんだか複雑そうだな…Google driveでよくない?

よくない.

Google driveでできないこと

  • 複数人で同じdirectoryの中身を編集できない(=分岐に対応していない)
  • RemoteとSyncするタイミングを制御できない(変更した瞬間自動的にSync)
  • revisionに含めるfileを制御できない(自動的にrevisionとして保存する対象に追加される)
  • これらができないがためにこうなりがち

恣意的な表

機能 Google Drive Git + Github
変更履歴(revision)の記録
記録するタイミングの制御 ×
記録するファイルの選択 ×
復元
分岐 ×
差分の表示 ×
履歴の共有

今日の目標

  • 何かやらかしても元に戻せるようになる

Gitは分散型Version管理ツール

👉 local元に戻せればremoteのも元に戻せる!

やること

  1. gitのコンセプトを理解する (頭で)
  2. 呪文を覚える (身体で)
  3. 呪文を打ち消す呪文を覚える (身体で)

2と3をひたすら繰り返す.

重犯罪人に自分の罪を思い知らせるなら、土を掘り移動させ、それからまた埋めて元に戻す、という作業を延々と繰り返させればよい。それは究極の拷問であり、最後には精神に異常をきたすであろう。(ドフトエフスキー?)

今日扱うcommandたち

1つのbranchの操作

revesion管理基礎

  • git status
  • git init
  • git add
  • git commit
  • git reset
  • git rm
  • git mv
  • git commit –amend
  • git rebase -i
複数のbranchの操作

分岐する場合を扱う

  • git log
  • git branch
  • git checkout
  • git merge
  • git rebase
remote branchの操作

Cloudを利用

  • git clone
  • git fetch
  • git pull
  • git push

Gitのコンセプトを理解する

Gitには3つの視点がある

  • Commit tree
  • Three areas
  • File status

これらを理解すればGit master.

Commit tree

保存されているCommit (=Version)の親子関係を表すtree (一番重要).

  • CommitはdirectoryのスナップショットにIDとしてhash値を振ったもの.
  • Commitには親となるCommitが記録されている (👉 tree構造)
  • 1度作ったcommitは基本的になくならない(探せなくはなるので注意)
  • Hash値だけで管理すると人間が使うとき不便なのでHEADとかmasterみたいな看板(refs)を使う
  • master, HEAD, branchなどは特定のCommitを指しているrefsの名前(重要)

refsのイメージ

  • 最後尾のCommit(人)は新しい人が並ぶたびに変わるが,最後尾は常に看板の指す先にある.

どういうときに思い出すべき?

  • 特定のversionを復元するとき (git reset, git checkout)
  • versionの分岐を扱うとき (git branch, git merge, git rebase)
  • remote branchと同期するとき (git push, git pull)

Three areas

1つのCommitの裏には3つのareaがある

  1. Working directory: 実際のdirectory (lsコマンドとかで見える風景)
  2. Staging Area (Index): Working direcoryのファイルのうち次のCommitに含まれる予定のファイルを一時保管する場所 (実際には,次のコミットに何が含まれるかに関しての情報を蓄えたファイル)
  3. git directory: Staging areaの情報を元に作られたCommitが保存される場所 (commit treeで見えているのはここの風景)

Staging areaとgit directoryはユーザから直接見えない!

どういうときに思い出すべき?

  • Commitを作るとき (git add, git commit)
  • version管理するファイルを選択するとき (git rm –cached, git add, .gitignore)
  • 一部のファイルのversionだけを戻すとき (git reset , git checkout )

File status

fileには4つの状態がある(three areasをfile側から見たもの)

  1. Untracked: gitがversion管理しないfile
  2. Unmodified: gitの管理対象かつ最新のversionから変更されていないfile
  3. Modified: gitの管理対象かつ最新のversionから変更されたfile(≒次Commitすべきもの)
  4. Staged: gitの管理対象かつ変更されたfileかつStaging areaに追加済みのもの(=次Commitされる!)

どういうときに思い出すべき?

  • gitの管理対象のディレクトリ内で,fileを生成,編集,削除したとき
  • fileを管理対象に入れたり外したりするとき (git add, git rm, git mv)

おめでとう

あなたは,gitマスターになりました.

Single branch 1 (add, commit, reset)

まえおき

ここから先の作業はSource treeのHistory(履歴)画面でCommit treeを表示しながらやることをオススメします.

まずは,versionが分岐しない場合,ただの便利なbackup toolとしてgitを扱う方法を体験しましょう.

この章で学べること

  • Working directoryにおけるfileの追加や変更を記録したCommitを作る方法
  • Commitに含めるファイルを選ぶ方法
  • これらをなかったことにする方法

学んだことからできること

  • Directoryの変更履歴を管理する(≒gitでdirectoryのbackupを取る)

git status

異世界に行ったら最初に唱える呪文.

File statusが4つのどの状態にあるかの確認.

これから何かする度になるべくgit statusしましょう.

思い出す

git init

current directoryにgit repositoryを作る

やってみる

やってみる

ls -a すると.git という隠しdirectoryが作られているはず.

これがgit repositoryであり,すべての変更履歴の情報はここに記録されていく(もっとも,直接見てわかるような形にはなっていない).

git initをなかったことにする

current directoryからgit repositoryを削除する

❗削除したrepositoryは元に戻せないので注意

git add

fileをStaging areaに追加する.

  • 新規ファイルの場合
    • tracked(gitの追跡対象指定)にする
    • staged(commitに含める対象指定)にする.
  • 既存ファイルの場合
    • staged(commitに含める対象指定)にする.

git add [path ...]

やってみる

git addをなかったことにする

  • 新規ファイルの場合
    • untracked(gitの追跡対象外)にする
    • unstaged(commitに含める対象外)にする.
  • 既存ファイルの場合
    • unstaged(commitに含める対象外)にする.

git reset [--soft | --hard] [<commit>] [path ...]

やってみる

stage(git add)されたすべてのファイルを対象にしたい場合

HEADは現在参照しているCommitを指すrefs(看板)

  • HEADが指しているCommitとことなるfileはmodified状態になる
  • HEADの指すCommitを変える=Working direcotryのfileをHEADの指すCommitに記録されているものに置き換える
  • HEAD^ = HEADの1つ前のCommit

git commit

stage (git add)されたすべてのfileの現在の状態をGit Directoryへ記録する

やってみる

git commit [--amend] [-m <msg>]

git commitをなかったことにする

Git Directoryから最後のCommitを削除する

やってみる

やってみる

addもなかったことに

commitとaddの2つをまとめてなかったことに

git commitをなかったことにしたのをなかったことに

復習: 1度作ったcommitは基本的になくならない(探せなくはなるので注意)

さっきなかったことにしたcommitは実は消えていない(みえなくなっただけ).

見えなくなったcommitを探すcommand = reflogを使う

やってみる

git reset

既に何度か使っているgit reset これの役割は?

💡 偉い人に聞きましょう(54ページくらいまで読み進めてください)

やりなおせる Git 入門 from Tomohiko Himura
HEAD インデックス 作業ディレクトリ 作業ディレクトリ保護の有無

Commit Level

reset –soft [commit]

REF

いいえ

いいえ

はい

reset [commit]

REF

はい

いいえ

はい

reset –hard [commit]

REF

はい

はい

いいえ

checkout [commit]

HEAD

はい

はい

はい

File Level

reset (commit) [file]

いいえ

はい

いいえ

はい

checkout (commit) [file]

いいえ

はい

はい

いいえ

つまり?

git reset --hard, git checkout <file> (上表で太字のいいえになっているやつ) するときは気をつけよう.

Single branch 2 (rm, mv)

この章で学べること

  • fileの削除や移動をgit repositoryへ記録する方法
  • 特定のfileをcommitに含めなくする方法

git rm –cached

fileをStaged areaとGit directoryから削除する

(=untracked(gitの追跡対象外)にする)

git rm [--cached] [path ...]

やってみる

実際のfileは削除せずにgitからfile000を削除(追跡対象外に)できた!

.gitignore

一生untrackedにしておきたいfileやdirectoryは,.gitignoreというfileを作ってそこに書く.

.gitignoreしたdirectory内の一部のfileのみをtrackedにしたい場合は,.gitkeepに書いてignoreされないようにする.

.gitignore(.gitkeep)については,自分でググってね. (i.e., あまり興味ない)

git rm

ファイルをWorking Directory, Staged area, Git directoryのすべてから削除する.

やってみる

上の2つをまとめてやることもできる

git mv

ファイルをWorking Directory, Staged area, Git directoryのすべてにおいてmoveまたはrenameする.

Note: mvとrenameは本質的に同じ

git mv <source> <destination>

これらをなかったことにする方法

commitをなかったことにするのと同じ

Single branch 3 (commit –amend, rebase)

この章で学べること

  • 作成済みのcommitを改変する方法
  • 改変をなかったことにする方法

学んだことからできること

  • gitに記録されているcommit(変更の歴史)を自由に改変する (≒single branchならなにをするのも怖くなくなる)

git commit –amend

最後のCommitを変更する

ファイルをaddし忘れたときとか,コメントを修正したい時とかに便利

やってみる

git commit –amendをなかったことにする

復習: 1度作ったcommitは基本的になくならない(探せなくはなるので注意)

amendは,一見直前のCommitを改変したように見えるが,実は新しくCommitを作って元々のCommitを見えなくしただけ.

そのため,見えなくなったcommitを探すcommand = reflogでamend前のCommitを復元することができる.

やってみる

HEADの指す位置をamend前のCommitに変える.

git rebase

直前だけじゃなく,もっと前まで遡って自分の都合の良いように歴史を改変したい

git rebase -i <commit>

<commit>からHEADまでのコミットをまとめて変更する

やってみる

git rebase -i <commit>

<commit>には変更の根本となるcommitを指定する.

この場合変更対象はHEAD - HEAD~3

1番目と2番目はまとめたくて,3番目のメッセージを書き直したくて,4番目は消したい.

git logを見てみると<commit>で指定した根本から枝分かれしていることがわかる

終わったらcontinue

git rebaseをなかったことにする

rebaseの途中でやっぱりやめたくなった場合

rebaseを発動する前の状態に戻ることができる.

rebase対象が多すぎて途中でわけわからなくなったら,素直に–abortして最初からやり直そう.

git rebaseをなかったことにする

rebaseしたあと,やっぱりなかったことにしたくなった

commit –amendと一緒で,rebaseは既存のcommitをコピーを作っているだけ

なので,元のcommitも残っている(見えなくなっているだけ).

やってみる

見えないcommitをたどるにはreflog.

git rebaseをなかったことにしたのをなかったことにする

一度はrebaseをなかったことにしたんだけど,よくよく考えるとrebaseした後の方が良い気がしてきた…(よく考えてから行動しましょう!)

同じ理屈でなかったことにしたのをなかったことにできる

やってみる

git rebaseまとめ

  • 指定したcommitを分岐点として改変された歴史作る
  • 途中でわけわかんなくなったら–abort

なんでもかんでもなかったことにできるようになった!

もう何も怖くない.

Multi branches (branch, checkout, merge, rebase)

まえおき

次に,branchingによりversionが分岐する場合,作業環境の管理toolとしてgitを扱う方法を体験しましょう.

gitのbranchingは,プログラミング言語における**envのように,仮想環境を作って実行環境を分離するのと似た効果をもたらします.

学んだことからできること

  • 複数人で一つのProjectを変更する (Remoteとの連携がMust)
  • Taskごとに作業環境を分離する

git log

基本の呪文 Commit treeを表示する

git log --oneline --graph --all

defaultのgit logは見にくいので,いい感じで表示してくれるwrapperもある.

logの表示だけでなくCommitとかを便利にできるWrapperもある

tig

思い出す

保存されているCommit (=Version)の親子関係を表すtree (一番重要).

  • CommitはdirectoryのスナップショットにIDとしてhash値を振ったもの.
  • Commitには親となるCommitが記録されている (👉 tree構造)
  • 1度作ったcommitは基本的になくならない(探せなくはなるので注意)
  • Hash値だけで管理すると人間が使うとき不便なのでHEADとかmasterみたいな看板(refs)を使う
  • master, HEAD, branchなどは特定のCommitを指しているrefsの名前(重要)

これからやる作業は基本的にrefsを作ったり移動したりする作業

git branch

という名前の新たなrefsを作り,を指すようにする

git branch [-d|-D] <branch-name> [<start-point>]

やってみる

feature/branch000という名前のrefを作る(指す先はmasterの指しているところ)

git branchをなかったことにする

branchを削除する.

未mergeのbranchは削除しようとするエラーが出る(二度と見つけられなくなるため)ので強制削除する必要がある.

強制的に削除する場合.

branchの削除をなかったことにする

git checkout

HEADの指す先を OR に変える.

git checkout [-b] [<branch>|<commit>]

やってみる

HEADの指す先をfeature/branch000へ変える

branchの作成とcheckoutをいっぺんにやる

checkoutで起こること

gitを使ったことのある人は,checkoutに他の枝(branch)に移るというイメージを持っているかもしれないが,実際にはHEADの指す先のrefsが変わるだけ.

👉 e.g., masterもdevelopも同じCommitを指していたら,どちらにcheckoutしてもWorking directoryは変化しない. 同じCommitをmasterというrefs越しに見るかdevelopというrefs越しに見るかが違うだけ.

CLIで見てみる

HEAD -> の指す先に注目.

<branch>にcheckoutした状態でCommitすると<branch>refsの位置はHEADに連動して移動する.

Commitしてみる

masterと違うCommitを指していることを確認

branchを変えて再度commitしてみる

分岐したことを確認

このようにしてTaskごとに作業環境を切り分けることができる.

詳説 checkout

厳密には,checkoutのやっていることは,ただHEADの指す先を変えるのとは少し違う.

checkoutは特定のbranch(もちろんcommitでも良い)からfileをWorking directoryに取り出してくるcommandである.

と書くことで,特定の<branch>(or <commit>)の<path>にあるfileを現在のWorking directoryに持ってくることができる.

<path>を省略すると,その<branch>の全ファイルをWorking directoryに持ってくることになる.

全部持ってきたらWorking directoryの中身は,その<branch>と全く同じになる.

👉 HEADを移動したのと同じ.

このため,未commitのfileがあるとcheckoutは失敗する

(まだcommitされていないfileがcheckout先のfileで上書きされてしまう!).

git merge

他のbranchと今いるbranchを統合するcommitを作る

git merge [<branch>]

git merge

Githubを使うのであればこの作業はPRをMergeしたときに勝手にやってくれる.

GithubでPRをmergeするときはこんな感じのことをしている.

PRのMergeで問題が起きたときに対処できるようになるために,GithubがPRをMergeするときの動作を自分でやってみる.

やってみる

Merge commitができている.

Merge commitがまたできている.

git mergeをなかったことにする

master tagとupdate_file000 tagをそれぞれMerge前の位置に戻せば良い.

(Interruption) HEADについて

(復習) HEAD^ = 1つ前の親Commit

Merge commitは親が2ついる. どうやって指定すれば?

親が2つ以上ある場合 HEAD^N (N=親の番号) のように指定できる.

HEAD~2と似てるけど,全然意味が違う.

HEADの表記法まとめ

  • HEAD~N = HEADのN個前の親Commit

  • HEAD^N = HEADの1個前の親CommitのN番目

ex) 1個前の親の2番めの親Commit = HEAD~1^2

  • } = N個前にHEADが指していたCommit

git mergeをなかったことにする (続き)

masterのMergeをなかったことに

update_file000のMergeをなかったことに

簡単ですね?

今までの話が理解できていれば,mergeをなかったことにしたのをなかったことにもできるはず.

git merge –no-ff について

このoptionはなんぞや

なしにしてやってみる

2回MergeしたのにMerge commitが1つしかできていない.は?

💡 偉い人に聞きましょう(99ページくらいまで読み進めてください)

こわくない Git from Kota Saito

git rebase

mergeと同じようなことをするcommandにrebaseがある.違いを見ましょう.

他のbranchの先端に今のbranchの根本から先端までのcommitをまとめて移植する

git rebase [<branch>]

git rebase --continue | --skip | --abort | --quit |

やってみる

masterとfeatureは統合されたが,Merge commitが一個もできていない

merge: Merge Commitを作って2つのbranchを統合する

rebase: 2つのbranchのどちらかをもう片方の先端に移植することで統合する

git rebaseをなかったことにする

まずはmasterを戻す.

次にupdate_file000を戻す

Mergeより戻すのが面倒(reflog使わないかん)ですね.

このことから,rebaseとmergeならmergeを使うべきという人もいます.

公式曰く,

公開リポジトリにプッシュしたコミットをリベースしてはいけない

この指針に従っている限り、すべてはうまく進みます。もしこれを守らなければ、あなたは嫌われ者となり、友人や家族からも軽蔑されることになるでしょう。(Pro Git)

詳説 rebase

💡 偉い人に聞きましょう(181ページくらいまで読み進めてください)

こわくない Git from Kota Saito

歴史改変のrebaseとbranchのrebase

歴史改変のrebase(左)とbranchのrebase(右) やっていることは同じ.

git cherry-pick

他のbranchの特定のcommitを今のbranchの先端に移植する

git cherry-pick <commit>...

やってみる

masterのcommitがfeature/update_file000へcopyされた

rebaseはcherry-pickを複数まとめてやっているだけとも言える.

Conflict

統合する複数のbranchで同じfileが変更されていると,CONFLICTが起こる

これは,Gitがどちらの歴史(Branch)が正しいか判断できないため.

userは自分で正しい歴史を選択する必要がある.

Conflictを起こしてみる

masterとfeatureの両方でfile000が編集されている状態を作り出す.

🎉 CONFLCIT!!!

Conflictしたfileの中身

Conflictの解消法

  1. ConfilitしたファイルをEditerで編集する
  2. git checkout [--theirs|--ours] <paths> を使う
  3. merge-toolを使う

1. ConfilitしたファイルをEditerで編集する

そのまんま. Vimとかでひらいて↓こんな感じに編集して保存,AddしてCommit

2. checkoutを使う方法

merge元のbranch(=–theirs)の変更かmerge先(=HEAD, –ours)の変更かのどちらかを採用する.

利点: pdfや画像などテキストとして編集できないものがConflictしたときに便利

PDFをVimで開いて「この行はmerge先を採用して,こっちはbranchを採用…」とかやってるやつがいたら病気…

欠点: どういう結果になるか予想がつきにくい.

3. merge-toolを使う

merge-toolには色々ある.

git mergetool --tool=vimdiff

DiffがあるChunk(=色が変わっている部分)にカーソルを合わせ, 1do, 2do, 3doと入力することで,それぞれ上段の左から1番目,2番目,3番目が今後の姿に取り込まれる.

PyCharmのようにConflictを解消するツールが付いているIDEもある.

Conflictを解消したら当該のfileをaddしてcommit.

❗Conflict解消のときは自動的にmessageが入力されるのでmオプションはいらない

成功すればMerge(or rebase, cherry-pickなど)が完了する.

Github

Githubってなんだよ

GitHub は世界最大の Remote Git リポジトリホスティングサービスだよ.

つまり?

localのrepository = localのPCにおいてあるdirecotry

とすると,

Github上のrepository = Google driveにおいてあるdirecotry

のような関係.

有名どころ

  • Github

  • Gitlab

  • Bitbucket

Githubがやたら有名だが他にもある (Google driveとDropbox的な関係).

できることは大差ないので,お財布事情とUIの好みの問題

(あとは狸と猫のどっちが好きかとか…バケツ…).

Github

  • とにかく有名 (連携サービスが多いイメージ).

  • お金たくさん持ってく.

  • Github Educationとか言う学生プランに加入するとGithub含め色々な有名サービスが無料で使える.

  • 本田圭佑がいる

  • 他の2つと違い自前のCIとかはない(他のツールと組み合わせる前提OSSの思想的には○?)

Gitlab

  • どちらかというと,自組織のサーバにインストールして自分たちしかアクセスできないRemote repositoryとして使う (要するに自前のfile server的なやつ)オンプレミス版のほうが有名.

  • Slackを水で薄めたようなchatアプリやCIがついてくるので,これひとつで開発に必要なすべての作業を完結させることができる.

  • 大人の事情でPublic臭の強いGithubとかSlackとかを使えない人たちが使ってるっぽい(NASAとか).

Bitbucket

  • 一昔前までPrivate repository(外部公開されない) 作り放題な唯一のサービスだったので,個人の利用者が多くいた(今は,Githubも作り放題).

  • Source treeの開発元でもあるAtlassianの製品.

  • お金ないらしい(最近値上げした).

比較

https://stackshare.io/stackups/bitbucket-vs-github-vs-gitlab

2019 2月現在,各サービスの無料プランの状況

Github Bitbucket Github (学生) Bitbucket (学生) GitLab
private repository 無制限 無制限 無制限 無制限 無制限
public repository 無制限 無制限 無制限 無制限 無制限
collaborator 3名まで 5名まで 無制限 無制限 無制限
CI build time - 50分/月 - 500分/月 2000分/月
LFS 1GB 1GB 1GB 5GB 無制限?
LFS Bandwidth 1GB/月 無制限 1GB/月 無制限 無制限?

Githubの基本操作

Issue

Issueを起てる

PRを送る

PRを送る2

PRをreviewする

PRをreviewする2

PRをreviewする3

PRをreviewする4

Remote branch (clone, fetch, pull, push)

まえおき

最後に,remote repositoryと連携する方法を体験しましょう.

remoteとの連携はlocalのcommit treeをremoteに同期することと同義です.

最も単純なケースでは,localで更新されたtreeをそのままremoteにcopyして,remoteで更新されたtreeをlocalにcopyすることの繰り返しに近い動作になります.

Gitは分散型なので,remoteと連携する場合でもほとんどの作業はlocalで完結するようにできています.

localだけの作業と変わる部分は,<remote>/<branch>という特殊なrefsが追加されることだけです.

このrefsは<remote> repositoryにおいて<branch>が指しているcommitを指します.

remoteでこのbranchに新しいcommitが追加されれば,当然この<remote>/<branch>の指す先もその新しいcommitに更新されます.

この新しいcommitを取り込みたければ,<remote>/<branch>をlocalの同じ名前の<branch>にmergeするだけですみます.

つまり,remoteとはただのbranchの一つ(ただし,自分以外の誰かの手によって勝手に更新される)だと考えることができます.

この基本原則を忘れなければ,localの操作の延長線上でremoteの操作も可能です.

なお,特に設定なければ<remote>にはoriginという名前が使われます.

これは,master branchが勝手にmasterという名前になっているのと同じで,ただのデフォルト値であり,特別な意味はありません.

この章で学べること

  • localの変更をremoteに同期する方法
  • その逆

学んだことからできること

  • remote repository経由で複数人と共同作業する
  • 複数のPCで同じrepositoryをUbiquitous(死語)に操作する

git clone

remote repositoryのcopyをlocalに作る.(= Download)

git clone <repository>

なにが起きるか

最初の一回しか使わない(あとはpullやfetch).

git cloneをなかったことにする

git fetch

現在のremoteの<branch> refsの位置に合わせてlocalのorigin/<branch> refsの位置を更新する.

git fetch <remote> <branch>

git fetch

このままではremoteのmaster(origin/master)がlocalのmasterに反映されていないので,masterをorigin/masterにmergeする.

git fetchをなかったことにする

remoteに合わせてlocalのrefsとcommitを更新するだけなので,git commitgit branchをしたのと起こることは同じ.

よって, git commit OR git branchをなかったことにするときと同じ方法でなかったことにできる.

git pull

remote branchの変更をlocalのbranchに取り込む (Downloadに近い)

git pull [--rebase|--no-ff] <remote> <branch>

git fetchgit merge origin/master をまとめてやる(--rebaseつけるとgit rebase origin/masterになる)

Mergeの作業が入っているため,pullではConflictが発生する可能性がある.

ちなみに,GithubのPRという名前はGithubのPRのMergeにpull(fetch+merge)コマンドが含まれることに由来する

git pullをなかったことにする

git mergeをなかったことにする方法 + git fetchをなかったことにする方法.

git push

local branchの変更をremote branchに反映させる (Uploadに近い)

git push <remote> <branch>

git pullのremoteとlocalを逆にしたもの(pullと違いConflictする場合は実行できない= –ff-only).

git pushできないとき

何れかの原因でConflictする場合,pushはできないので適切な方法で対処する

自分に原因がある場合

  • 原因となる作業: rebase, commit –amend, resetなどremoteとconflictする作業をした
  • 対処法: git push -f <remote> <branch>, git push --force-with-lease <remote> <branch> で強制pushする

git push -f は強制的にremoteを上書きするので,ミスるとremoteからすべての歴史を吹き飛ばすので(頭真っ白になる), 吹き飛ばしても人に迷惑のかからないfeature branchのみで使うこと.

--force-with-lease オプションはローカルのrefsがremoteと一致している場合のみ(i.e., localが最新の場合)強制的にremoteを上書きするのでやや安全

他人に原因がある場合

  • 原因となる作業: 同じbranchに他の人がpushした, remote repository上でCommitした
  • 対処法: git pull <remote> <branch> してConflictを解消してからpushする (pullはconflictが起こっても実行できるため)

git pushをなかったことにする

pushをなかったことにするには,さらにpushするしかない.

git push -f をして,push前の状態を強制上書きする.

もちろん,reset --hardで,localのcommitをなかったことにしてからpush -f してもよい.

あれ,さっきpush -f するなって言わなかったっけ?

そうです.なかったことにしなくて良いように,よく考えてからpushしましょう.

git fetch VS git pull

  • 他のbranchにcheckoutせずにremoteのCommitを取り込みたい場合fetch
  • それ以外の場合pull

最新のmasterにfeatureをrebaseしたい場合の例

4 command -> 2 commmand

特に,HEADに未commitのfileがあるときにこれをやりたくなるとpullでは非常に面倒(stashを使えばいいのだが更に2 command増える)

Branching model

Branching model (branch戦略)とは?

branchをどのように切るかを決めるstyleの一つ.

別に守らなくてもErrorは起きないが,守ると便利なルール(Coding styleとかと同じ立ち位置).

Git Flow

  • feature:
    • 作業用branch
    • Issueをassignされた人が好きにして良い
    • Issueと対応しているとbetter
  • master:
    • relase専用のbranch
    • ここに直接CommitしたりfeatureをMergeすることはない
  • develop:
    • feature branchをmergeする専用のbranch
    • ここに直接commitすることはない
  • (hotfixes):
    • masterに緊急のbugが見つかったときに修正のcommitをするbranch
  • (release):
    • masterにmergeする直前の作業をするbranch

Github Flow

featureとmasterしかないシンプルなFlow.

面倒なことはPR上でやる.

Gitlab flow

  • feature/hotfix: 機能開発、不具合対応branch
  • master: mainのbranch
  • pre-production(option): release前のtest用(git-flowで言うrelease branch)
  • production: release済みのcode置き場

Gitでやってはいけないこと

sizeのでかいファイルをcommitに含める

cloneするのにすごく時間がかかるようになります.

やっちゃったらGitの内側 - メインテナンスとデータリカバリを参考に当該のファイルを削除する(失敗するとCommitツリーを破壊するリスクがあるのでしないにこしたことはない)

Tips: Git LFS という大容量バイナリファイルを扱う方法も提供されている

developやmasterでrebaseする

featuredevelopやmasterrebaseするのはおk

developやmasterでgit push -fする

Githubでは,push -fできないようにbranchをprotectすることも可

やってしまっても大概なんとかなるけど,他の人にすごく迷惑がかかるので,喧嘩売りたいとき以外はやめましょう.

Gitで積極的にやるべきこと

即Issueを起てる,即Branchを切る

何かしようと思ったらとりあえずIssue起てる.

Commit messageにIssue番号を含める

Added some files. #1 のようにすると1番のIssueのページから当該のCommitが見れるようになりトラッキングが楽.

労う

よいPRには,LGTM (Looks Good To Me) を送りましょう.

Exercise

Git golf

なるべく少ないgit commandsでホールを回るのが目標.

模範解答のcommand数をPARxで表している(一部,バーディやイーグルも可).

Rules:

  • 同じことができれば使用するコマンドは自由にして良い
  • ググるの禁止(gitのhelpを見るのは推奨)
  • 次のコマンドはカウントされない
    • git status
    • git log
    • git diff
    • git show
    • gitで始まらないコマンド
    • rebase中のコマンド(i.e., rebateは1回とカウントされる)

1番ホール PAR3

  • remoteをcloneする
  • masterでfile000を編集してcommitする

2番ホール PAR4

  • branch(feature/hoge)を切ってそのbranchにcheckoutする
  • file001を削除してcommitする

3番ホール PAR2

  • file000をfile001にrenameして先程のcommitをamendする

4番ホール PAR3

  • masterをcloneしてきたときの状態にresetする

(branch切るの忘れて作業してたので,あとから取り繕った体. masterの指す位置がclone直後と同じになっていればよい)

(masterではなくfeature branchにcheckoutした状態で終わる)

5番ホール PAR5

  • 誰かによって更新されたremoteのmasterをpullしてきて,現在のbranchに変更を取り込む(rebaseする)
  • remoteにpushする

回答(パー)

Hole 1

Hole 2

Hole 3

Hole 4

Hole 5

回答(アンダー)

Hole 1

Hole 2

Hole 3

Hole 4

Hole 5

もっといい方法あったら教えてください.

Advanced

gitの小技N本ノック

gitの小技N本ノック

特に以下の3つのコマンド(keyword)については一読しておいたほうが良い.

  • stash
  • revert
  • submodule (これとgit clone –recursiveを知らないと人のGithubのコードをcloneして使うときとかに詰まることが稀によくある)

References

勉強に使えるサイト

Pro Git

  • Git公式サイトの管理者が書いた本(無料で見れる)
  • 一次資料に近い情報なので一通り読むことが推奨される(ちょっと仔細にわたり過ぎているきらいはあるのでいきなり読まないほうが良いかも)
  • 日本語(もある)

Git をはじめからていねいに

  • 手を動かしながらGit commandを学べる
  • 詳しすぎず雑すぎずちょうどよい説明
  • 日本語

Learning Git Branching

  • GitのBranchをシミュレートしてくれるWeb application
  • 一通りのBranch操作のチュートリアルがある
  • Branchを視覚的に理解する役に立つ
  • Chromeだと表示がおかしくなる(Firefoxは平気だった)
  • 英語(日本語もあるけどなんかバグってる)

やりなおせる Git 入門

こわくない Git